#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "pwmdriver.h"

int  major = 0;
struct class *cls = NULL;
struct device *dev = NULL;

static struct resource *rescon;
static struct resource *restimer;
static unsigned int *gpd0con;
static void *timer_base;

char kbuf[] = {'1', '2', '3', '4'};

#define TCFG0 0x0000
#define TCFG1 0x0004
#define TCON 0x0008
#define TCNTB0 0x000C
#define TCMPB0 0x0010

int flags = 1;

#define DEBUG_PRINTK(msg,DEBUG_FLAG) \
    do{ \
        if(!!DEBUG_FLAG) { \
            printk("---->%s--->%d\n",__func__,__LINE__);\
            printk(msg);\
		}\
    }while(0)

void fs4412_beep_on(unsigned int *tcon)
{
	writel(readl(tcon) | (1<<0), tcon);
}

void fs4412_beep_off(unsigned int *tcon)
{
	writel(readl(tcon) | (~(1<<0)), tcon);
}

static void fs4412_beep_freq(unsigned int *tcntb0, int beep_tcnt, unsigned int *tcmpb0, int beep_tcmp)
{
	writel(beep_tcnt, tcntb0);
	writel(beep_tcmp, tcmpb0);
}

int pwm_driver_open(struct inode *pnode, struct file *pfile)
{
	printk("pwm_driver_open is called\n");
	return 0;
}

int pwm_driver_close(struct inode *pnode, struct file *pfile)
{
	printk("pwm_driver_close is called\n");
	fs4412_beep_off(timer_base + TCON);
	return 0;
}

static ssize_t pwm_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)
{
	int ret = 0;

	ret = copy_to_user(buf, kbuf, 4);
	if(ret)
	{
		printk("copy_to_user failed\n");
		return -1;
	}
	return 4;
}

static ssize_t pwm_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)
{
	int ret = 0;

	ret = copy_from_user(kbuf, buf, 4);
	if(ret)
	{
		printk("copy_from_user failed\n");
		return -1;
	}
	printk("copy_from_user buf = %s\n", kbuf);

	return 4;
}

long pwm_ioctl(struct file *pfile, unsigned int cmd, unsigned long args)
{
	beep_desc_t *beeper = (beep_desc_t*)args;
	// printk("pwn_ioctl is called!\n");
	switch (cmd)
	{
	case BEEP_ON:
		fs4412_beep_on(timer_base + TCON);
		break;
	case BEEP_OFF:
		fs4412_beep_off(timer_base + TCON);
		break;
	case BEEP_FREQ:
		fs4412_beep_freq(timer_base + TCNTB0, beeper->tcnt, timer_base + TCMPB0, beeper->tcmp);
		break;
	
	default:
		return -EINVAL;
	}

	return 0;
}

struct file_operations pwm_ops = {
	.owner = THIS_MODULE,
	.open = pwm_driver_open,
	.release = pwm_driver_close,
	.read = pwm_read,
	.write = pwm_write,
	.unlocked_ioctl = pwm_ioctl
};

void fs4412_beep_init(void)
{
	writel((readl(gpd0con) & ~(0xF<<0)) | (0x2<<0), gpd0con);
	writel((readl(timer_base + TCFG0) & ~(0xFF<<0)) | (0xF9<<0), timer_base + TCFG0);
	writel((readl(timer_base + TCFG1) & ~(0xF<<0)) | (0x2<<0), timer_base + TCFG1);

	writel(100, timer_base + TCNTB0);
	writel(80, timer_base + TCMPB0);

	writel(readl(timer_base + TCON) | (1<<3), timer_base + TCON);
	writel(readl(timer_base + TCON) | (1<<1), timer_base + TCON);
	writel(readl(timer_base + TCON) & ~(1<<1), timer_base + TCON);
}

int pwm_driver_probe(struct platform_device *pdev)
{
    // printk("--->%s--->%d.\n",__func__,__LINE__);

	rescon = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	restimer = platform_get_resource(pdev, IORESOURCE_MEM, 1);

	gpd0con = ioremap(rescon->start, rescon->end - rescon->start);
	timer_base = ioremap(restimer->start, restimer->end - restimer->start);

	major = register_chrdev(0, "pwmdriver", &pwm_ops);
	if(major <= 0)
	{
		printk("register_chrdev failed\n");
		return -1;
	}

	cls = class_create(THIS_MODULE, "fsbeeper");
	if(cls == NULL)
	{
		printk("class_create failed\n");
		return -1;
	}

	dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "fsbeeper%d", 0);
	if(dev == NULL)
	{
		printk("device_create failed\n");
		return -1;
	}

	fs4412_beep_init();

	return 0;
}

int pwm_driver_remove(struct platform_device *p_pltdev)
{
	iounmap(gpd0con);
	iounmap(timer_base);
	device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, "pwmdriver");
	printk("pwm_driver_remove is called\n");
	return 0;
}

struct of_device_id pwm_dts[] = {
	{.compatible = "fs4412,beeper"},
	{}
};

struct platform_device_id pwm_ids[] = {
	[0] = {"pwm3", 0},
	[1] = {}
};

struct platform_driver pwm_driver = {
	.driver = {
		.name = "pwmdriver",
		.owner = THIS_MODULE,
		.of_match_table = pwm_dts
	},
	.probe = pwm_driver_probe,
	.remove = pwm_driver_remove,

	.id_table = pwm_ids
};
module_platform_driver(pwm_driver);
MODULE_LICENSE("GPL");

